package com.gjzhao.fmmall.controller;

import com.gjzhao.fmmall.service.CategoryService;
import com.gjzhao.fmmall.service.IndexImgService;
import com.gjzhao.fmmall.service.ProductService;
import com.gjzhao.fmmall.vo.RespResultVO;
import com.gjzhao.fmmall.vo.ResponseResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description: 首页 Controller
 * @Author: gjzhao
 * @Date Created in 2022-01-01 9:52 下午
 */
@RestController
@RequestMapping("/index")
@Api(value = "提供首页所需要的数据的接口", tags = "首页管理")
public class IndexController {

    @Autowired
    private IndexImgService indexImgService;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private ProductService productService;


    @GetMapping("/indexImg")
    @ApiOperation(value = "首页轮播图接口")
    public ResponseResultVO listIndexImg() {
        ResponseResultVO responseResultVO = indexImgService.listIndexImg();
        return responseResultVO;
    }


    @GetMapping("/listIndexImgByStream")
    @ApiOperation(value = "首页轮播图接口(使用java8中的Stream流进行操作)")
    public RespResultVO listIndexImgByStream() {

        RespResultVO resultVO = indexImgService.listIndexImgByByStream();

        return resultVO;
    }

    /**
     * 接口作用：查询首页轮播图信息
     * 业务流程分析：轮播图的写操作，不是那么频繁
     *  - 1.先从数据库查询轮播图信息
     *      - 1.1 如果能够查询到信息，则直接返回给前端
     *      - 1.2 如果不能查询到信息
     *          - 1.2.1 从数据库中查询轮播图信息
     *          - 1.2.2 缓存到redis，同时设置过期时间为1day(1day后,redis中数据过期，需要重新去数据库查找进行缓存)
     *
     * @return
     */
    @GetMapping("/indexImgByRedis")
    @ApiOperation(value = "首页轮播图接口(使用redis方式)")
    public RespResultVO indexImgByRedis() {
        RespResultVO respResultVO = indexImgService.listIndexImgByRedis();
        return respResultVO;
    }

    /**
     * 接口作用：高并发场景下，大量请求访问首页去查询分类列表
     * 业务流程分析：
     *      - 1.用户进入首页，需要去查询分类列表，调用该接口
     *      - 2.首先从redis查询缓存的数据
     *          - 2.1 如果redis中存在数据，则直接返回给前端
     *          - 2.2 如果redis中不存在数据，则需要先从数据库中查询(但是在高并发场景下，此时可能有大量请求需要去数据库中查询)
     *              - 2.2.1 使用双重检测锁机制，从redis再查询一次，如果存在，直接返回
     *              - 2.2.2 如果不存在，此时再去数据库中进行查询，查询完成后，同时缓存到redis中(下一个请求过来时，便可以从redis中查询到数据)
     * @return
     */
    @GetMapping("/categoryListByRedisAndConcurrent1")
    @ApiOperation(value = "一次性查询所有分类级别信息(使用redis方式,适用高并发场景下——解决缓存击穿问题)")
    public RespResultVO categoryListByRedisAndConcurrent1() {
        RespResultVO respResultVO = categoryService.listCategoriesByRedisByAndConcurrent1();
        return respResultVO;
    }

    /**
     * 接口作用：高并发场景下，大量请求访问首页去查询分类列表
     * 业务流程分析：
     *      - 1.用户进入首页，需要去查询分类列表，调用该接口
     *      - 2.首先从redis查询缓存的数据
     *          - 2.1 如果redis中存在数据，则直接返回给前端
     *          - 2.2 如果redis中不存在数据，则需要先从数据库中查询(但是在高并发场景下，此时可能有大量请求需要去数据库中查询)
     *              - 2.2.1 使用双重检测锁机制，从redis再查询一次，如果存在，直接返回
     *              - 2.2.2 如果不存在，此时再去数据库中进行查询
     *                 - one ： 如果数据库依然查询不到，在redis中缓存一个非空数据，同时设置过期时间(避免数据已经入库，但缓存中还是无效数据的情况)
     *                 - two : 如果能够在数据库中查询到，将查询到的数据缓存到redis中(下一个请求过来时，便可以从redis中查询到数据)
     *
     * 注意点：在高并发场景下，如果要求强一致性，可以通过两次写操作来完成
     *      - 1.第一个线程在数据库没有找到数据时，设置一个非空数据，同时设置过期时间
     *      - 2.后面往数据库写入正常数据时，删除之前填充的非空数据，写入正常数据
     * @return
     */
    @GetMapping("/categoryListByRedisAndConcurrent2")
    @ApiOperation(value = "一次性查询所有分类级别信息(使用redis方式,适用高并发场景下——解决缓存击穿 + 缓存穿透)")
    public RespResultVO categoryListByRedisAndConcurrent2() {
        RespResultVO respResultVO = categoryService.listCategoriesByRedisByAndConcurrent2();
        return respResultVO;
    }



    @GetMapping("/categoryList")
    @ApiOperation(value = "一次性查询所有分类级别信息(连接查询-关联3次自己)")
    public ResponseResultVO listCategory() {
        ResponseResultVO responseResultVO = categoryService.listCategories();
        return responseResultVO;
    }

    /**
     *  接口作用：查询分类列表(使用redis方式)
     *  业务流程分析：分类列表的数据不会经常改动，将其缓存到redis中，同时设置过期时间为一周，如果分类信息需要改动，则清空redis中缓存的信息，下次查询重新从数据库中查找
     *      - 1.先从redis中查询分类数据
     *          - 1.1 如果是分类数据存在，则返回数据给前端
     *          - 1.2 如果分类数据不存在，则先从数据库中进行查询，同时缓存到数据库，设置过期时间为7day
     *
     * @return
     */
    @GetMapping("/categoryListByRedis")
    @ApiOperation(value = "一次性查询所有分类级别信息(使用redis方式)")
    public RespResultVO categoryListByRedis() {
        RespResultVO respResultVO = categoryService.listCategoriesByRedis();
        return respResultVO;
    }

    @GetMapping("/firstCategoryList")
    @ApiOperation(value = "查询所有一级分类列表")
    public ResponseResultVO listFirstCategory() {
        ResponseResultVO responseResultVO = categoryService.listFirstLevelCategories();
        return responseResultVO;
    }

    @GetMapping("/parentCategoryList")
    @ApiOperation(value = "查询该父级分类下的所有子类信息")
    @ApiImplicitParam(value = "父级id", name = "parentId", dataType = "int", required = true)
    public ResponseResultVO listParentCategories(@RequestParam("parentId") int parentId) {
        ResponseResultVO responseResultVO = categoryService.listParentCategories(parentId);
        return responseResultVO;
    }

    @GetMapping("/recommendsProductList")
    @ApiOperation(value = "新上架商品推荐列表")
    public ResponseResultVO listRecommendsProduct() {
        ResponseResultVO responseResultVO = productService.listRecommendsProduct();
        return responseResultVO;
    }


    @GetMapping("/FirstCategoryRecommendsProductList")
    @ApiOperation(value = "一级分类下的推荐商品列表(连接查询商品表/商品图片表)")
    public ResponseResultVO listFirstCategoryRecommendsProduct() {
        ResponseResultVO responseResultVO = categoryService.listFirstLevelCategoryRecommendsProduct();
        return responseResultVO;
    }

    @GetMapping("/categoryProductList")
    @ApiOperation(value = "查询该分类下的销量排名前6的商品信息")
    @ApiImplicitParam(value = "分类id", name = "category_id", dataType = "string", required = true)
    public ResponseResultVO listCategoryProducts(@RequestParam("category_id") String categoryId) {
        ResponseResultVO responseResultVO = productService.listCategoryProduct(categoryId);
        return responseResultVO;
    }


}
